package u2

import (
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

// Detail 表示差异比较的详细信息
// 用于记录JSON结构比较中的具体差异点
type Detail struct {
	Left   string `json:"left"`   // 左侧值（被比较的原始值）
	Right  string `json:"right"`  // 右侧值（比较的目标值）
	Key    string `json:"key"`    // 差异点的JSON路径
	Style  string `json:"style"`  // 差异类型：change/delete/add/type/error
	Ignore bool   `json:"ignore"` // 是否忽略该差异
}

// String 实现Stringer接口，格式化差异信息
// 输出格式根据差异类型自动调整，便于阅读和日志记录
func (v Detail) String() string {
	var result strings.Builder
	result.WriteString(fmt.Sprintf("%s: [%s] ", v.Key, v.Style))
	switch v.Style {
	case "error":
		if v.Key == "left" {
			result.WriteString(fmt.Sprintf("%s\n", v.Left))
		} else {
			result.WriteString(fmt.Sprintf("%s\n", v.Right))
		}
	case "type":
		result.WriteString(fmt.Sprintf("%s -> %s\n", v.Left, v.Right))
	case "change":
		result.WriteString(fmt.Sprintf("%s -> %s\n", v.Left, v.Right))
	case "delete":
		result.WriteString(fmt.Sprintf("%s\n", v.Left))
	case "add":
		result.WriteString(fmt.Sprintf("%s\n", v.Right))
	default:
		result.WriteString(fmt.Sprintf("%s -> %s\n", v.Left, v.Right))
	}
	return result.String()
}

// JsonDiff 记录JSON差异比较的结果和状态
// 包含完整的比较结果和运行时状态信息
type JsonDiff struct {
	HasDiff  bool                                          // 是否存在差异
	result   strings.Builder                               // 结果缓冲区（线程安全）
	Details  []*Detail                                     `json:"details"` // 差异细节列表
	Cost     string                                        `json:"cost"`    // 比较耗时（格式化字符串）
	sttime   time.Time                                     // 比较开始时间（内部使用）
	Ignore   []string                                      `json:"ignore"` // 忽略的差异列表
	KeyMap   map[string]string                             `json:"keyMap"` // 键名映射表（左->右）
	valueMap map[string]func(left, right interface{}) bool // 自定义值比较函数表
	Speed    int                                           `json:"speed"` // 比较速度等级（1-5）
	debug    bool                                          // 调试模式开关（内部使用）
}

// addResult 安全地追加结果字符串到缓冲区
// 内部方法，用于构建差异结果字符串
func (j *JsonDiff) addResult(s string) {
	j.result.WriteString(s)
}

// printCost 打印操作耗时信息
// 仅在调试模式下输出，用于性能分析和优化
// 参数：
//
//	name - 操作名称标识符，用于区分不同的计时点
func (j *JsonDiff) printCost(name string) {
	if j.debug {
		fmt.Println(name, "cost:", formatDuration(time.Since(j.sttime)))
	}
}

// 优化耗时显示格式
func formatDuration(d time.Duration) string {
	switch {
	case d < time.Millisecond:
		return fmt.Sprintf("%dμs", d.Microseconds())
	case d < time.Second:
		return fmt.Sprintf("%.2fms", float64(d.Milliseconds())/1000)
	case d < time.Minute:
		return fmt.Sprintf("%.2fs", d.Seconds())
	default:
		minutes := int(d.Minutes())
		seconds := d.Seconds() - float64(minutes*60)
		return fmt.Sprintf("%dm%.2fs", minutes, seconds)
	}
}

// getKeyFromRootKey 从根节点获取对应的值
// 支持直接匹配和正则匹配，处理数组索引的通用化
// 参数：
//
//	key - 要查找的键路径
//	checkMap - 要查找的映射表
//
// 返回值：
//
//	interface{} - 找到的值，未找到返回nil
func getKeyFromRootKey(key string, checkMap interface{}) interface{} {
	if checkMap == nil {
		return nil
	}
	var tmpMap map[string]interface{}
	switch ev := checkMap.(type) {
	case map[string]interface{}:
		tmpMap = ev
	case map[string]string:
		tmpMap = make(map[string]interface{})
		for k, v := range ev {
			tmpMap[k] = v
		}
	case map[string]func(left, right interface{}) bool:
		tmpMap = make(map[string]interface{})
		for k, v := range ev {
			tmpMap[k] = v
		}
	default:
		return nil
	}
	//直接匹配
	if v, ok := tmpMap[key]; ok {
		return v
	} else {
		//正则匹配
		newkey := key
		if strings.Contains(newkey, "[") && strings.Contains(newkey, "]") {
			rex, _ := regexp.Compile(`\[\d+?\]`)
			newkey = rex.ReplaceAllString(newkey, "[x]")
		}
		//遍历匹配
		for tmpKey, v := range tmpMap {
			if tmpKey == newkey {
				return v
			}
		}
		return nil
	}
}

// Ignores 忽略某些字段的比较
// keys: 要忽略的字段名, 可以是多个;$.xxx.xx list:$.xxx[x],写死 x 就行
// 如果需要指定类型：$.xxx.xx:type，这里添加:，后面的 type 被指定,可多个:，不然默认全部忽略
// 类型有：change,delete,add,type,error,all,其他默认不忽略
// Ignores 设置要忽略的差异规则
// 参数：
//
//	keys - 忽略规则列表，支持格式：
//	       "json.path"        - 忽略该路径所有差异
//	       "json.path:style"  - 忽略特定类型的差异
//
// 返回值：
//
//	JsonDiff - 新的差异比较实例（不可变设计）
//
// 注意：多次调用会合并忽略规则
func (d JsonDiff) Ignores(keys ...string) JsonDiff {
	ignoreLine := 0
	for _, key := range keys {
		var ignoreStyle bool
		style := make(map[string]bool)
		sp := strings.Split(key, ":")
		for i := 1; i < len(sp); i++ {
			style[sp[i]] = true
			if sp[i] == "all" {
				ignoreStyle = true
			}
		}

		if len(sp) == 1 {
			ignoreStyle = true
		}
		for _, v := range d.Details {
			newkey := v.Key
			if strings.Contains(newkey, "[") && strings.Contains(newkey, "]") {
				rex, _ := regexp.Compile(`\[\d+?\]`)
				newkey = rex.ReplaceAllString(v.Key, "[x]")
			}
			if newkey == sp[0] {

				if _, ok := style[v.Style]; ok || ignoreStyle || v.Ignore {
					v.Ignore = true

					ignoreLine++
					if !v.Ignore {
						d.Ignore = append(d.Ignore, v.String())
					}
					continue
				}
			}
		}
	}

	if d.HasDiff && len(d.Details) == ignoreLine {
		return JsonDiff{HasDiff: false, result: d.result, Cost: d.Cost, Details: d.Details, sttime: d.sttime, Ignore: d.Ignore}
	}
	return d
}

// PrintAll 打印所有的比较结果
// PrintAll 打印完整的差异报告到标准输出
// 包含所有差异细节和统计信息，适合调试使用
func (d JsonDiff) PrintAll() {
	fmt.Println("\n-----------------diff-----------------")
	df := d.DetailsString(true)
	fmt.Println(df)
	if df != "No diff" {
		fmt.Println(d.result.String())
	}
	fmt.Println("Compare cost:", d.Cost, "\n-----------------diff-----------------")
}

// Result2File 将比较结果写入文件
func (d JsonDiff) Result2File(filepath string, name string) {
	sss := d.ResultString(name)
	err := os.WriteFile(filepath, []byte(sss), 0666)
	if err != nil {
		fmt.Println("write file error:", err)
	}
}

// ResultString 将比较结果以字符串的形式返回
func (d JsonDiff) ResultString(name string) string {
	sss := "\n" + name + "-----------------diff-----------------\n"
	df := d.DetailsString(false)
	sss += df
	if df != "No diff" {
		sss += d.result.String()
	}
	sss += "\nCompare cost:" + d.Cost + "\n" + name + "-----------------diff-----------------"
	return sss
}

// String 以字符串的形式返回比较结果
// String 实现Stringer接口，返回基本差异信息
// 适合快速查看主要差异，不包含细节信息
func (d JsonDiff) String() string {
	return d.result.String()
}

// DetailsString 以字符串的形式返回比较的细节
func (d JsonDiff) DetailsString(showIgnore bool) string {
	var result string
	for _, v := range d.Details {
		if v.Ignore {
			if showIgnore {
				fmt.Println("ignore", strings.Trim(v.String(), "\n"))
			}
			continue
		}
		result += v.String()
	}
	if result == "" {
		return "No diff"
	}
	return "//说明：[change]是字段有变化；[delete]是原对象多的字段；[add]是原对象少的字段\n\n" + result
}

// DeepEqual 深度比较两个对象是否相等
// DeepEqual 深度比较两个值的相等性
// 使用reflect.DeepEqual实现，处理各种嵌套类型的比较
// 参数：
//
//	left - 要比较的第一个值
//	right - 要比较的第二个值
//
// 返回值：
//
//	bool - 是否深度相等
func DeepEqual(left, right interface{}) bool {
	return reflect.DeepEqual(left, right)
}

// JsonCompareWithIgnore 比较两个JSON结构，忽略指定路径的差异
// 参数：
//
//	left - 左侧JSON数据
//	right - 右侧JSON数据
//	ignoreKeys - 要忽略的路径列表
//
// 返回值：
//
//	JsonDiff - 差异比较结果
func JsonCompareWithIgnore(left, right interface{}, ignoreKeys ...string) JsonDiff {
	diff := JsonCompare(left, right)
	return diff.Ignores(ignoreKeys...)
}

/*
# JsonComparePlus 比较两个 json 结构的不同

  - left: 左边的 json 结构
  - right: 右边的 json 结构
  - keyMap: key 映射，key 是左边的 key，value 是右边的 key
  - valueMap: value 映射，key 是 key，value 是比较函数,返回 true 表示相等，false 表示不相等;如果是 nil，使用默认的比较函数,默认是 DeepEqual;left和right是数字时，请转成 int64 比较
*/
func JsonComparePlus(left, right interface{}, keyMap map[string]string, valueMap map[string]func(left, right interface{}) bool, speed int) JsonDiff {
	diff := &JsonDiff{HasDiff: false, result: strings.Builder{}, Details: make([]*Detail, 0), sttime: time.Now(), Ignore: make([]string, 0), KeyMap: keyMap, valueMap: valueMap, Speed: speed}
	diff.result.Grow(1024 * 1024)
	diff.addResult("\n")
	diff.debug = false
	if diff.valueMap == nil {
		diff.valueMap = make(map[string]func(left, right interface{}) bool)
	}
	if diff.KeyMap == nil {
		diff.KeyMap = make(map[string]string)
	}
	if diff.Speed <= 0 {
		diff.Speed = 1
	}
	if diff.Speed > 5 {
		diff.Speed = 5
	}
	diff.printCost("JsonComparePlus_init")
	switch left.(type) {
	case map[string]interface{}:
		if _, ok := right.(map[string]interface{}); !ok {
			diff.HasDiff = true
			diff.addResult("\n-" + Struct2JsonNoError(left, false) + ",")
			diff.addResult("\n+" + Struct2JsonNoError(right, false))
			diff.Details = append(diff.Details, &Detail{Left: Struct2JsonNoError(left, false), Right: Struct2JsonNoError(right, false), Key: "$", Style: "type"})
		} else {
			jsonDiffDict(left.(map[string]interface{}), right.(map[string]interface{}), 1, "$", diff)
		}
	case []interface{}:
		if _, ok := right.([]interface{}); !ok {
			diff.HasDiff = true
			diff.addResult("\n-" + Struct2JsonNoError(left, false) + ",")
			diff.addResult("\n+" + Struct2JsonNoError(right, false))
			diff.Details = append(diff.Details, &Detail{Left: Struct2JsonNoError(left, false), Right: Struct2JsonNoError(right, false), Key: "$", Style: "type"})
		} else {
			jsonDiffList(left.([]interface{}), right.([]interface{}), 1, "$", diff)
		}
	case string:
		var left_interface interface{}
		err := B2I([]byte(left.(string)), &left_interface)
		if err != nil {
			jsonDiffCommon(left, right, "", "$", "$", diff)
		} else {
			var right_interface interface{}
			err = B2I([]byte(right.(string)), &right_interface)
			if err != nil {
				jsonDiffCommon(left, right, "", "$", "$", diff)
			} else {
				return JsonComparePlus(left_interface, right_interface, diff.KeyMap, diff.valueMap, 1)
			}
		}

	default:
		jsonDiffCommon(left, right, "", "$", "$", diff)
	}
	if diff.HasDiff {
		diff.addResult("\n")
	}
	diff.Cost = formatDuration(time.Since(diff.sttime))
	return *diff
}

// JsonCompare 比较两个 json 结构的不同
func JsonCompare(left, right interface{}) JsonDiff {
	return JsonComparePlus(left, right, nil, nil, 1)
}

// jsonDiffDict 比较两个字典结构
// 递归处理嵌套的字典结构，支持键名映射
// 参数：
//
//	left - 左侧字典
//	right - 右侧字典
//	depth - 当前递归深度
//	rootkey - 根路径
//	diff - 差异记录器
func jsonDiffDict(left, right map[string]interface{}, depth int, rootkey string, diff *JsonDiff) {

	if left == nil && right == nil {
		return
	}
	if left == nil {
		left = make(map[string]interface{})
	}
	if right == nil {
		right = make(map[string]interface{})
	}
	diff.addResult("{")
	blank := strings.Repeat(" ", (3 * (depth)))
	longBlank := blank + " "
	diff.printCost(longBlank + "jsonDiffDict_st_" + rootkey)
	defer diff.printCost(longBlank + "jsonDiffDict_end_" + rootkey)
	tmpMap := make(map[string]string)
	// 对key 排序
	keys := make([]string, 0)
	for key := range left {
		rootkeyT := rootkey + "." + key
		newKey := getKeyFromRootKey(rootkeyT, diff.KeyMap)
		if newKey != nil {
			tmpMap[key] = newKey.(string)
			tmpMap[newKey.(string)] = key
		} else {
			tmpMap[key] = key
		}
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, key := range keys {
		value := left[key]
		key = tmpMap[key]
		quotedKey := fmt.Sprintf("\"%s\"", key)
		quotedKey_source := fmt.Sprintf("\"%s\"", tmpMap[key])

		rootkeyT := rootkey + "." + tmpMap[key]
		if _, ok := right[key]; ok {
			switch value.(type) {
			case map[string]interface{}:
				if _, ok2 := right[key].(map[string]interface{}); !ok2 {
					diff.HasDiff = true
					diff.addResult("\n-" + blank + quotedKey_source + ": " + Struct2JsonNoError(value, false) + ",")
					diff.addResult("\n+" + blank + quotedKey + ": " + Struct2JsonNoError(right[key], false))
					diff.Details = append(diff.Details, &Detail{Left: Struct2JsonNoError(value, false), Right: Struct2JsonNoError(right[key], false), Key: rootkeyT, Style: "type"})
				} else {
					diff.addResult("\n" + longBlank + quotedKey + ": ")
					jsonDiffDict(value.(map[string]interface{}), right[key].(map[string]interface{}), depth+1, rootkeyT, diff)
				}
			case []interface{}:
				diff.addResult("\n" + longBlank + quotedKey + ": ")
				if _, ok2 := right[key].([]interface{}); !ok2 {
					diff.HasDiff = true
					diff.addResult("\n-" + blank + quotedKey + ": " + Struct2JsonNoError(value, false) + ",")
					diff.addResult("\n+" + blank + quotedKey + ": " + Struct2JsonNoError(right[key], false))
					diff.Details = append(diff.Details, &Detail{Left: Struct2JsonNoError(value, false), Right: Struct2JsonNoError(right[key], false), Key: rootkeyT, Style: "type"})
				} else {
					jsonDiffList(value.([]interface{}), right[key].([]interface{}), depth+1, rootkeyT, diff)
				}
			default:
				jsonDiffCommon(value, right[key], longBlank, quotedKey_source+": ", rootkeyT, diff)
			}
		} else {
			diff.HasDiff = true
			diff.Details = append(diff.Details, &Detail{Left: Struct2JsonNoError(value, false), Right: "", Key: rootkeyT, Style: "delete"})
			diff.addResult("\n-" + blank + quotedKey + ": " + Struct2JsonNoError(value, false))
		}
		diff.addResult(",")
	}
	// 检查多出的字段
	for key, value := range right {
		rootkeyT := rootkey + "." + key
		if _, ok := tmpMap[key]; !ok {
			diff.HasDiff = true
			diff.Details = append(diff.Details, &Detail{Left: "", Right: Struct2JsonNoError(value, false), Key: rootkeyT, Style: "add"})
			diff.addResult("\n+" + blank + "\"" + key + "\"" + ": " + Struct2JsonNoError(value, false) + ",")
		}
	}
	// 移除最后一个逗号
	resultStr := diff.result.String()
	if strings.HasSuffix(resultStr, ",") {
		diff.result.Reset()
		diff.result.WriteString(resultStr[:len(resultStr)-1])
	}
	if depth == 1 {
		diff.addResult("\n" + blank[0:len(blank)-2] + "}")
	} else {
		diff.addResult("\n" + blank[0:len(blank)-1] + "}")
	}
}

// jsonDiffList 比较两个列表结构
// 处理数组/列表的逐项比较，支持并行加速
// 参数：
//
//	left - 左侧列表
//	right - 右侧列表
//	depth - 当前递归深度
//	rootkey - 根路径
//	diff - 差异记录器
func jsonDiffList(left, right []interface{}, depth int, rootkey string, diff *JsonDiff) {
	blank := strings.Repeat(" ", (3 * (depth)))
	diff.printCost(blank + "jsonDiffList_st_" + rootkey)
	defer diff.printCost(blank + "jsonDiffList_end_" + rootkey)
	diff.addResult("[")
	size := len(left)
	if size > len(right) {
		size = len(right)
	}
	addSpeed := 0
	if size > 100 && size < 3000 && diff.Speed == 1 {
		addSpeed = 1
	} else if size > 3000 && diff.Speed < 3 {
		addSpeed = 3 - diff.Speed
	}
	NewFor(nil).Num(0, size-1, diff.Speed+addSpeed, func(i int) bool {
		rootkeyT := rootkey + "[" + strconv.Itoa(i) + "]"
		switch left[i].(type) {
		case map[string]interface{}:
			if _, ok := right[i].(map[string]interface{}); ok {
				jsonDiffDict(left[i].(map[string]interface{}), right[i].(map[string]interface{}), depth+1, rootkeyT, diff)
			} else {
				diff.HasDiff = true
				diff.addResult("\n-" + blank[0:len(blank)-1] + Struct2JsonNoError(left[i], false) + ",")
				diff.addResult("\n+" + blank[0:len(blank)-1] + Struct2JsonNoError(right[i], false))
				diff.Details = append(diff.Details, &Detail{Left: Struct2JsonNoError(left[i], false), Right: Struct2JsonNoError(right[i], false), Key: rootkeyT, Style: "type"})
			}
		case []interface{}:
			if _, ok2 := right[i].([]interface{}); !ok2 {
				diff.HasDiff = true
				diff.addResult("\n-" + blank[0:len(blank)-1] + Struct2JsonNoError(left[i], false) + ",")
				diff.addResult("\n+" + blank[0:len(blank)-1] + Struct2JsonNoError(right[i], false))
				diff.Details = append(diff.Details, &Detail{Left: Struct2JsonNoError(left[i], false), Right: Struct2JsonNoError(right[i], false), Key: rootkeyT, Style: "type"})
			} else {
				jsonDiffList(left[i].([]interface{}), right[i].([]interface{}), depth+1, rootkeyT, diff)
			}
		default:
			jsonDiffCommon(left[i], right[i], blank, "", rootkeyT, diff)
		}
		if i != size-1 {
			diff.addResult(",")
		}
		return true
	})
	for i := size; i < len(left); i++ {
		diff.HasDiff = true
		diff.addResult("\n-" + blank[0:len(blank)-1] + Struct2JsonNoError(left[i], false))
		if i != len(left)-1 {
			diff.addResult(",")
		}

		diff.Details = append(diff.Details, &Detail{Left: Struct2JsonNoError(left[i], false), Right: "", Key: rootkey + "[" + strconv.Itoa(i) + "]", Style: "delete"})
	}
	for i := size; i < len(right); i++ {
		diff.HasDiff = true
		diff.addResult("\n+" + blank[0:len(blank)-1] + Struct2JsonNoError(right[i], false))
		if i != len(right)-1 {
			diff.addResult(",")
		}
		diff.Details = append(diff.Details, &Detail{Left: "", Right: Struct2JsonNoError(right[i], false), Key: rootkey + "[" + strconv.Itoa(i) + "]", Style: "add"})
	}
	// 移除最后一个逗号
	resultStr := diff.result.String()
	if strings.HasSuffix(resultStr, ",") {
		diff.result.Reset()
		diff.result.WriteString(resultStr[:len(resultStr)-1])
	}
	diff.addResult("\n" + blank[0:len(blank)-2] + "]")
}

// jsonDiffCommon 比较两个基本类型的值
// 处理字符串、数字等基本类型的比较，支持自定义比较函数
// 参数：
//
//	left - 左侧值
//	right - 右侧值
//	longBlank - 格式化用的缩进字符串（仅在字典比较时使用）
//	key - 当前键名（仅在字典比较时使用）
//	rootkey - 根路径
//	diff - 差异记录器
//
// 返回值：
//
//	无，结果直接记录到diff中
func jsonDiffCommon(left, right interface{}, longBlank string, key, rootkey string, diff *JsonDiff) {
	// 初始化空白字符
	blank := ""
	if longBlank == "" {
		longBlank = " "
	} else {
		blank = longBlank[0 : len(longBlank)-1]
	}
	// 记录性能耗时
	diff.printCost(blank + "jsonDiffCommon_st_" + rootkey)
	defer diff.printCost(blank + "jsonDiffCommon_end_" + rootkey)

	// 获取自定义比较函数
	fc := getKeyFromRootKey(rootkey, diff.valueMap)
	var nfc func(interface{}, interface{}) bool
	if fc != nil {
		nfc = fc.(func(interface{}, interface{}) bool)
	}
	if nfc == nil {
		nfc = DeepEqual
	}

	// 处理json.Number类型，统一转换为int64进行比较
	le, ok := left.(json.Number)
	re, ok2 := right.(json.Number)
	if ok && ok2 {
		left = le.String()
		right = re.String()
	}
	// 执行比较
	if !nfc(left, right) {
		// 记录差异
		diff.HasDiff = true
		diff.addResult("\n-" + blank + key + Struct2JsonNoError(left, false) + ",")
		diff.addResult("\n+" + blank + key + Struct2JsonNoError(right, false))
		diff.Details = append(diff.Details, &Detail{
			Left:  Struct2JsonNoError(left, false),
			Right: Struct2JsonNoError(right, false),
			Key:   rootkey,
			Style: "change",
		})
	} else {
		// 记录相同值
		diff.addResult("\n" + blank + key + Struct2JsonNoError(left, false))
	}
}

// LoadJson 从文件加载JSON数据
// 参数：
//
//	path - 文件路径
//	dist - 目标结构体指针
//
// 返回值：
//
//	error - 加载过程中发生的错误
func LoadJson(path string, dist interface{}) error {
	if content, err := os.ReadFile(path); err == nil {
		err = B2I(content, dist)
		return err
	} else {
		return err
	}
}
